using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Input;

namespace Deflection
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        SpriteFont LCDDisplayGrid;
        ScreenManager screenManager;
        PlayerFactory playerFactory;
        EnemyFactory enemyFactory;
        WaveFactory waveFactory;
        MediaLibrary BGMusicLibrary = new MediaLibrary();
        SongCollection BGMusic;
        SongCollection sFX;
        BackgroundManager backgroundManager;
        bool playedPauseNoise = false;

        ProjectileFactory projectileFactory;


        public List<Screen> Screens = new List<Screen>();
        public enum SFX
        {
            Shoot1,
            Shoot2,
            Shoot3,
            Explode1,
            Explode2,
            Explode3,
            MenuItemSelect,
            MenuItemStartGame,
            Pause,
        }
        public enum ScreenEnum
        {
            StartScreen = 0,
            PauseScreen = 1,
            ScoreScree = 2
        }


        public InputHandler inputHandler;

        public enum GameState
        {
            StartScreen,
            LoadScreen,
            Playing,
            Pause,
            GameOver,
            ScoreBoardScreen
        }
        public static GameState State { get; set; }

        
        
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // Instance factories
            Paths.Instance();
            Formations.Instance();
            screenManager = ScreenManager.Instance();
            playerFactory = PlayerFactory.Instance();
            enemyFactory = EnemyFactory.Instance();
            backgroundManager = BackgroundManager.Instance();
            projectileFactory = ProjectileFactory.Instance();
            waveFactory = WaveFactory.Instance();



            // Change screem res
            this.graphics.PreferredBackBufferHeight = (int)GameParameters.SCREEN_SIZE.Y;
            this.graphics.PreferredBackBufferWidth = (int)GameParameters.SCREEN_SIZE.X;
            graphics.ApplyChanges();

            // Input handler
            Components.Add(new InputHandler(this));
            // Frame Rate counter
            Components.Add(new FrameRateCounter(this));

            State = GameState.StartScreen;
            StartScreen startScreen = new StartScreen();
            startScreen.Selection += new StartScreen.SelectionHandler(ImplementSelection);
            Screens.Add(startScreen);


            PauseScreen pauseScreen = new PauseScreen();
            pauseScreen.Selection += new StartScreen.SelectionHandler(ImplementSelection);
            Screens.Add(pauseScreen);

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            LCDDisplayGrid = Content.Load<SpriteFont>("Fonts/LCDDisplayGrid");
            GameParameters.GAME_FONT = Content.Load<SpriteFont>("Fonts/LCDDisplayGrid");
            backgroundManager.LoadContent(Content);
            Song BGMusic1 = Content.Load<Song>("Audio/Music/Deflection, the Musical");
            Screen.MenuItemSelect = Content.Load<SoundEffect>("Audio/Effects/Menuitemselect");
            Screen.Pause = Content.Load<SoundEffect>("Audio/Effects/Pause");
            Screen.StartGame = Content.Load<SoundEffect>("Audio/Effects/Menustartgame");
            MediaPlayer.IsShuffled = false;
            MediaPlayer.IsRepeating = true;
            MediaPlayer.Play(BGMusic1);
            MediaPlayer.Volume = 0.25f;
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }
        public void ImplementSelection(MenuItems selection)
        {
            switch(selection)
            {
                case MenuItems.OnePlayer:
                    playerFactory.LoadContent(Content);
                    playerFactory.CreatePlayer(1);
                    break;
                case MenuItems.TwoPlayers:
                    playerFactory.LoadContent(Content);
                    playerFactory.CreatePlayer(1);
                    playerFactory.CreatePlayer(2);
                    break;
                case MenuItems.Exit:
                    this.Exit();
                    break;

                case MenuItems.Continue:
                    State = GameState.Playing;
                    break;
                default:
                    break;
            }
            State = GameState.LoadScreen;
        }
        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // TODO: Add your update logic here
            this.IsMouseVisible = true;
            backgroundManager.LoadContent(Content);
            switch(State)
            {
                case GameState.StartScreen:
                    //state = GameState.LoadScreen;
                    Screens[(int)ScreenEnum.StartScreen].Update(gameTime);
                    backgroundManager.Update(gameTime);
                    break;
                case GameState.LoadScreen:
                    //call loadContents here

                    enemyFactory.LoadContent(Content);
                    projectileFactory.LoadContent(Content);

                    State = GameState.Playing;
                    break;
                case GameState.Playing:
                    backgroundManager.Update(gameTime);
                    playerFactory.Update(gameTime);
                    enemyFactory.Update(gameTime);
                    projectileFactory.Update(gameTime);
                    waveFactory.Update(gameTime);


                    break;
                case GameState.Pause:
                    Screens[(int)ScreenEnum.PauseScreen].Update(gameTime);
                    if (!playedPauseNoise)
                    {
                        Screen.Pause.Play();
                        playedPauseNoise = true;
                    }
                    // Dont update any of the game elements
                    break;
                case GameState.GameOver:
                    break;
                case GameState.ScoreBoardScreen:
                    break;
                default:
                    break;
            }
            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.Beige);

            spriteBatch.Begin(SpriteSortMode.BackToFront,BlendState.AlphaBlend);
            
            
            switch (State)
            {
                case GameState.StartScreen:
                    //state = GameState.LoadScreen;
                    Screens[(int)ScreenEnum.StartScreen].Draw(gameTime, spriteBatch);
                    backgroundManager.Draw(gameTime, spriteBatch);
                    break;
                case GameState.LoadScreen:

                    // Show progress bar ?
                    
                    break;
                case GameState.Playing:
                    backgroundManager.Draw(gameTime, spriteBatch);
                    playerFactory.Draw(gameTime, spriteBatch);
                    enemyFactory.Draw(gameTime, spriteBatch);
                    projectileFactory.Draw(gameTime, spriteBatch);
                    waveFactory.Draw(gameTime, spriteBatch);

                    break;
                case GameState.Pause:

                    Screens[(int)ScreenEnum.PauseScreen].Draw(gameTime, spriteBatch);
                    // Still draw elements, but they arnt being updated
                    backgroundManager.Draw(gameTime, spriteBatch);
                    playerFactory.Draw(gameTime, spriteBatch);
                    enemyFactory.Draw(gameTime, spriteBatch);
                    projectileFactory.Draw(gameTime, spriteBatch);
                    waveFactory.Draw(gameTime, spriteBatch);

                    break;
                case GameState.GameOver:
                    break;
                case GameState.ScoreBoardScreen:
                    break;
                default:
                    break;
            }

            spriteBatch.End();


            base.Draw(gameTime);
        }
    }

}
